package edu.hawaii.ics.pjdb.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.thrift.TDeserializer;
import org.apache.thrift.TException;
import org.apache.thrift.TSerializer;
import org.apache.thrift.protocol.TJSONProtocol;
import org.slf4j.Logger;

import au.com.bytecode.opencsv.CSVReader;

import edu.hawaii.ics.pjdb.types.Plan;
import edu.hawaii.ics.pjdb.types.Tuple;

public class IOHelper {
    /**
     * Recursively remove directory
     * 
     * Taken from: http://www.exampledepot.com/egs/java.io/DeleteDir.html
     * 
     * @param dir
     *            The directory to be deleted.
     * @return If the directory has been successfully deleted.
     */
    public static boolean deleteDir(File dir) {
        if (dir.isDirectory()) {
            String[] children = dir.list();
            for (int i = 0; i < children.length; i++) {
                boolean success = deleteDir(new File(dir, children[i]));
                if (!success) {
                    return false;
                }
            }
        }

        // The directory is now empty so delete it
        return dir.delete();
    }

    /**
     * Deletes all the specified directories (recursively)
     * 
     * @param dirs
     *            The directories to be deleted.
     * @return If the directory has been sucessfully deleted.
     */
    public static boolean deleteDirs(File... dirs) {
        for (File dir : dirs) {
            if (!deleteDir(dir)) {
                return false;
            }
        }

        return true;
    }

    /**
     * Gets the entire contents of the specified {@link Reader} and puts it into
     * a {@link String}. Catches the {@link IOException} and re-throws it as a
     * {@link RuntimeException}. (Naughty, I know.)
     * 
     * @param reader
     * @return
     * @throws IOException
     */
    public static String readerToString(Reader reader) {
        try {
            StringBuilder sBuilder = new StringBuilder();
            BufferedReader lineReader = new BufferedReader(reader);

            String line;
            while ((line = lineReader.readLine()) != null) {
                sBuilder.append(line);
                sBuilder.append("\n");
            }

            return sBuilder.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Parses the JSON representation of the execution plan in the given file
     * into a Java {@link Plan} object.
     * 
     * @param fileName
     *            Name of the file containing the JSON representation of the
     *            plan.
     * @return A Java Plan object that can be run on the system.
     * @throws TException
     *             If unable to deserialize into a Plan.
     */
    public static Plan readPlan(String fileName) throws TException {
        try {
            String file = IOHelper.readerToString(new FileReader(fileName));
            Plan plan = new Plan();
            TDeserializer deserializer = new TDeserializer(
                    new TJSONProtocol.Factory());

            deserializer.fromString(plan, file);
            return plan;
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Writes the given {@link Plan} in JSON form to the file with the given
     * name.
     * 
     * @param plan
     *            The Plan to be serialized.
     * @param fileName
     *            The name of the file that the serialized plan should be
     *            written to.
     * @throws IOException
     *             If unable to perform file operations (i.e. write to the
     *             file).
     * @throws TException
     *             If unable to serialize the plan.
     */
    public static void exportPlan(Plan plan, String fileName)
            throws IOException, TException {
        TSerializer serializer = new TSerializer(new TJSONProtocol.Factory());
        FileWriter fw = new FileWriter(fileName);
        fw.write(serializer.toString(plan));
        fw.close();
    }

    /**
     * Reads the tuples in a CSV file into a List of Tuples.
     * 
     * @param csvFile
     *            The path to the CSV file that should be read.
     * @param header
     *            If the CSV file contains a header that should be skipped.
     * @param logger
     *            The {@link Logger} used to record any error messages.
     * @return The list of tuples from the CSV file if successful or null if an
     *         error was encountered.
     */
    public static List<Tuple> readCsvFile(String csvFile, boolean header,
            Logger logger) {
        try {
            ArrayList<Tuple> tuples = new ArrayList<Tuple>();
            // Read CSV Values
            CSVReader reader = new CSVReader(new FileReader(csvFile));
            String[] nextLine;

            // Skip over header.
            if (header) {
                reader.readNext();
            }

            nextLine = reader.readNext();
            while (nextLine != null) {
                tuples.add(new Tuple(Arrays.asList(nextLine)));
                nextLine = reader.readNext();
            }
            return tuples;
        } catch (FileNotFoundException e) {
            logger.info("Error: Could not find CSV file \"{}\"", csvFile);
            return null;
        } catch (IOException e) {
            logger.info("Error: I/O problem occurred when reading from CSV file.");
            return null;
        }
    }
}
